﻿using System;
using System.IO;
using System.Xml;
using System.Xml.Linq;
using OpenXml.Internal;
using OpenXml.Internal.IO.Zip;
using OpenXml.IO.Packaging;

namespace OpenXml.IO.CTProviders
{
    public class XMindContentTypeProvider : ContentTypeProvider
    {
        private static readonly string FORWARD_SLASH = "/";

        private readonly String _contentTypeFileName = "META-INF/manifest.xml";
        private readonly String _elementName = "manifest";
        private readonly String _namespaceName = "urn:xmind:xmap:xmlns:manifest:1.0";
        private readonly String _nodeName = "file-entry";
        private readonly String _attrFullPathName = "full-path";
        private readonly String _attrMediaTypeName = "media-type";

        internal XMindContentTypeProvider() { }

        public XMindContentTypeProvider(ZipArchive zipArchive) : base(zipArchive) { }

        public override string ContentTypeFileName
        {
            get
            {
                return _contentTypeFileName;
            }
        }

        public override ContentTypeProvider Create(ZipArchive zipArchive)
        {
            return new XMindContentTypeProvider(zipArchive);
        }

        protected override void ParseContentType(Stream ctStream)
        {
            using (ctStream)
            {
                using (XmlReader xmlReader = new XmlTextReader(ctStream))
                {
                    XDocument document = XDocument.Load(xmlReader);
                    foreach (XElement element in document.Elements())
                    {
                        if (element.Name.LocalName.Equals(_elementName, StringComparison.Ordinal)
                            && element.Name.NamespaceName.Equals(_namespaceName, StringComparison.Ordinal))
                        {
                            foreach (XElement el in element.Elements())
                            {
                                if (el.Name.LocalName.Equals(_nodeName, StringComparison.Ordinal))
                                {
                                    XAttribute fullPath = el.Attribute(XName.Get(_attrFullPathName));
                                    XAttribute mediaType = el.Attribute(XName.Get(_attrMediaTypeName));

                                    if (fullPath != null && !String.IsNullOrEmpty(fullPath.Value) && mediaType != null)
                                    {
                                        if (String.IsNullOrEmpty(mediaType.Value))
                                        {
                                            this.DefaultDictionary.Add(fullPath.Value, new ContentType(mediaType.Value));
                                        }
                                        else
                                        {
                                            PackUriHelper.ValidatedPartUri key = PackUriHelper.ValidatePartUri(
                                                new Uri(CorrectPath(fullPath.Value), UriKind.Relative));
                                            this.OverrideDictionary.Add(key, new ContentType(mediaType.Value));
                                        }
                                    }
                                    else
                                    {
                                        throw new FormatException("Xmind file format incorrect");
                                    }
                                }
                                else
                                    throw new FormatException("Xmind file format incorrect");
                            }
                        }
                        else
                            throw new FormatException("Xmind file format incorrect");
                        break;
                    }
                }
            }
        }

        private static String CorrectPath(String path)
        {
            return path[0].Equals(FORWARD_SLASH) ? path : String.Concat(FORWARD_SLASH, path);
        }

        public static XMindContentTypeProvider Empty
        {
            get
            {
                return new XMindContentTypeProvider();
            }
        }
    }
}
